<?php
// +----------------------------------------------------------------------
// | 使用命令行执行控制器方法 [命令行可携带参数或者不携带]
// | 命令行运行方法：php think action admin/test/index --option name=jack,age=10
// | 控制器接收参数方法：
// | public function index(array $params = [])
// | {
// |    var_dump($params);
// | }
// +----------------------------------------------------------------------

declare (strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\Exception;

class Action extends Command
{

    protected function configure()
    {
        $this->setName("action")->addArgument("route", Argument::OPTIONAL, "your run route path!")//路由地址必须输入
        ->addOption('option', 'o', Option::VALUE_REQUIRED,
            'set Controller Argument') // 额外选项 用来传递方法参数 --option name=yourname,password=yourpass
        ->setDescription("Command run Controller Action !");
    }

    protected function execute(Input $input, Output $output)
    {
        try {
            $arguments = $input->getArguments();

            if ($arguments['command'] !== 'action') {
                throw new Exception("方法不存在");
            }

            $class_fun      = $this->route($arguments['route']);
            $namespaceClass = "{$class_fun['app']}\\{$class_fun['module']}\\{$class_fun['controller']}\\{$class_fun['class']}";
            $fun            = $class_fun['fun'];

            if ( ! class_exists($namespaceClass)) {
                throw new Exception("Class {$namespaceClass} not found.");
            }

            $classObj = app($namespaceClass);

            if ( ! method_exists($classObj, $fun)) {
                throw new Exception("Method {$fun} not found in class {$namespaceClass}.");
            }

            if ($input->hasOption('option')) {
                $params = $this->option(trim((string)$input->getOption('option')));
            } else {
                $params = [];
            }

            // 修改调用方式，直接传递参数数组
            if (method_exists($classObj, $fun)) {
                $result = $classObj->$fun($params);
            } else {
                throw new Exception("Method {$fun} not found in class {$namespaceClass}.");
            }

            $output->writeln(is_scalar($result) ? (string)$result : json_encode($result, JSON_UNESCAPED_UNICODE));

        } catch (\ArgumentCountError $d) {
            $output->writeln("Function error or not exist! class = {$namespaceClass} && func = {$fun}");
        } catch (\Exception $e) {
            $output->writeln($e->getMessage());
        }
    }

    public function route($route = '')
    {
        // 初始化默认返回值
        $class_fun = [
            'app'        => 'app',
            'module'     => 'index',
            'controller' => 'controller',
            'class'      => 'index',
            'fun'        => 'index'
        ];

        if ($route) {
            $app        = "app";
            $controller = "controller";
            $route      = explode("/", $route);
            $module     = isset($route[0]) ? $route[0] : "index";
            $class      = isset($route[1]) ? $route[1] : 'index';
            $fun        = isset($route[2]) ? $route[2] : 'index';

            // 将类名和方法名转换为大写驼峰命名格式
            $class = $this->toCamelCase($class);
            $fun   = $this->toCamelCase($fun);

            $class_fun = [
                'class'      => $class,
                'fun'        => $fun,
                'module'     => $module,
                'app'        => $app,
                'controller' => $controller
            ];
        }

        return $class_fun;
    }

    public function option(string $option): array
    {
        $params = [];

        if (empty($option)) {
            return $params;
        }

        $option_arr = explode(",", $option);

        foreach ($option_arr as $val) {
            if (str_contains($val, '=')) {
                [$key, $value] = explode("=", $val, 2);
                $params[$key] = $value;
            }
        }

        return $params;
    }

    /**
     * 将下划线命名转换为大写驼峰命名
     *
     * @param string $str
     *
     * @return string
     */
    private function toCamelCase($str)
    {
        // 将字符串按_分割成数组
        $arr = explode('_', $str);
        // 将每个单词首字母大写并连接
        $result = '';
        foreach ($arr as $word) {
            $result .= ucfirst(strtolower($word));
        }

        return $result;
    }

}
